package com.lwj.degest;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Base64Utils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;

/**
 * 摘要签名
 *
 * @author lwj
 *
 */
public class DigestSignature {
	private Gson gson = Utils.getGson();
	private DigestHelper digestHelper;

	/**
	 * @param secret 秘钥
	 */
	public DigestSignature(String secret) {
		digestHelper = new DigestHelper(secret);
	}

	/**
	 * 编码
	 *
	 * @param entity
	 * @return
	 * @throws IOException
	 */
	public String encodeEntity(DigestEntity entity) throws IOException {
		if (entity == null) {
			return null;
		}
		signature(entity);
		String json = gson.toJson(entity);
		return json;
	}

	/**
	 * 解码
	 *
	 * @param json
	 * @return
	 * @throws Exception
	 */
	public DigestEntity decodeEntity(String json) throws Exception {
		Type type = new TypeToken<DigestEntity>() {
		}.getType();
		DigestEntity entity = gson.fromJson(json, type);
		verify(entity);
		return entity;
	}

	/**
	 * 编码
	 *
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	public String encode(Object obj) throws Exception {
		String json = encodeJson(obj);
		String base64 = Base64Utils.encodeToString(json.getBytes());
		return base64;
	}
	/**
	 * 编码
	 *
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	public String encode(Object obj,int minute) throws Exception {
		String json = encodeJson(obj,minute);
		String base64 = Base64Utils.encodeToString(json.getBytes());
		return base64;
	}
	/**
	 * 编码
	 *
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	public String encodeJson(Object obj,int minute) throws Exception {
		DigestEntity digestEntity = new DigestEntity(minute);
		digestEntity.setEntity(obj);
		return encodeEntity(digestEntity);
	}

	/**
	 * 解码
	 *
	 * @param base64
	 * @param clazz
	 * @return
	 * @throws Exception
	 */
	public <T> T decode(String base64, Class<T> clazz) throws Exception {
		byte[] bytes = Base64Utils.decodeFromString(base64);
		String json = new String(bytes, StandardCharsets.UTF_8);
		T t = decodeJson(json, clazz);
		return t;
	}

	/**
	 * 编码
	 *
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	public String encodeJson(Object obj) throws Exception {
		DigestEntity digestEntity = new DigestEntity();
		digestEntity.setEntity(obj);
		return encodeEntity(digestEntity);
	}

	/**
	 * 解码
	 *
	 * @param json
	 * @param clazz
	 * @return
	 * @throws Exception
	 */
	public <T> T decodeJson(String json, Class<T> clazz) throws Exception {
		DigestEntity digestEntity = decodeEntity(json);
		return digestEntity.getEntity(clazz);
	}

	/**
	 * 对字符串生成摘要
	 *
	 * @param message
	 * @return
	 */
	protected String sha256(String message) {
		return digestHelper.sha256(message);
	}

	/**
	 * 添加签名
	 *
	 * @param entity
	 * @return
	 */
	protected DigestEntity signature(DigestEntity entity) {
		if (entity == null) {
			return null;
		}
		String sign = sha256(entity.toString());
		entity.setSign(sign);
		return entity;
	}

	/**
	 * 校验
	 *
	 * @param entity
	 * @throws Exception
	 */
	protected void verify(DigestEntity entity) throws Exception {
		String verifySign = sha256(entity.toString());
		if (!verifySign.equals(entity.getSign())) {
			throw new Exception("sign.error");
		}
		long now = System.currentTimeMillis();
		if (entity.getExp() < now) {
			throw new Exception("expire");
		}
	}

}

/**
 * 摘要类
 *  使用HmacSHA256，获取数据摘要
 *
 * @author lwj
 *
 */
class DigestHelper {
	private Logger logger = LoggerFactory.getLogger(DigestHelper.class);
	private final String HMACSHA256 = "HmacSHA256";
	/** SHA256实例 */
	private Mac hmacSHA256 = null;

	/**
	 * 初始化
	 *
	 * @param secret 秘钥
	 */
	public DigestHelper(String secret) {
		try {
			hmacSHA256 = Mac.getInstance(HMACSHA256);
			SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), HMACSHA256);
			hmacSHA256.init(secret_key);
		} catch (Exception e) {
			logger.error("DigestHelper hs256 init Error.", e);
		}
	}

	/**
	 * <p>
	 * sha256摘要<br>
	 * 压测: 10w 数据耗时 1.224s
	 * </p>
	 *
	 * @param message 消息
	 * @return 字符串hs256摘要
	 */
	public String sha256(String message) {
		String hash = "";
		try {
			byte[] bytes = hmacSHA256.doFinal(message.getBytes());
			hash = byteArrayToHexString(bytes);
		} catch (Exception e) {
			logger.error("DigestHelper message hs256 Error.", e);
		}
		return hash;
	}

	/**
	 * 将加密后的字节数组转换成字符串
	 *
	 * @param b 字节数组
	 * @return 字符串
	 */
	private String byteArrayToHexString(byte[] b) {
		StringBuilder hs = new StringBuilder();
		String stmp;
		for (int n = 0; b != null && n < b.length; n++) {
			stmp = Integer.toHexString(b[n] & 0XFF);
			if (stmp.length() == 1)
				hs.append('0');
			hs.append(stmp);
		}
		return hs.toString().toLowerCase();
	}
}
